/**
 * 
 */
package com.gragra.functionalIdentification;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap;
import it.unimi.dsi.fastutil.ints.IntIterator;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import com.gragra.data.Corpus;
import com.gragra.frameStatistics.CoOccurrenceStatistics;
import com.gragra.frameStatistics.StatisticsGenerator;
import com.gragra.nounIdentification.ConvergenceByIterations;
import com.gragra.nounIdentification.IncreasingMaxWithMin;
import com.gragra.nounIdentification.LinearIncreasingMax;
import com.gragra.nounIdentification.NounIdentifier;
import com.gragra.nounIdentification.Scorer;
import com.gragra.sampling.sequentialStructure.ExtensionInformationHolder;
import com.gragra.sampling.sequentialStructure.ExtensionInformationValue;
import com.gragra.sampling.sequentialStructure.ExtensionLevels;
import com.gragra.sampling.sequentialStructure.WordAndTagHolder;
import com.gragra.sampling.sequentialStructure.WordType;
/**
 * @author Christoph Teichmann
 * created Mar 26, 2013 3:50:44 PM
 * @version 0.1
 */
public class FunctionalInsteadOfNoun extends NounIdentifier
{
	/**
	 * 
	 *
	 * @param wth
	 * @param j
	 * @return
	 */
	protected boolean decideTypePresentBefore(WordAndTagHolder wth, int j)
	{return this.nouns.contains(wth.getWord(j));}
	/* (non-Javadoc)
	 * @see com.gragra.nounIdentification.BaseNounIdentifier#createStatistics(com.gragra.data.Corpus, int, int, int)
	 */
	@Override
	protected StatisticsGenerator createStatistics(
			Corpus<WordAndTagHolder> data, int frameSize, int preMarkers,
			int postMarkers)
	{return new CoOccurrenceStatistics(data, frameSize, preMarkers, postMarkers);}
	/**
	 * 
	 * @param data
	 * @param frameSize
	 * @param preMarkers
	 * @param postMarkers
	 * @param sco
	 * @param ca
	 * @param ma
	 */
	public FunctionalInsteadOfNoun(Corpus<WordAndTagHolder> data,
			int frameSize, int preMarkers, int postMarkers, int maxWords, int maxFrames, int number)
	{
		super(data, frameSize, preMarkers, postMarkers, new LowTypeTokenScorer(), new ConvergenceByIterations(3),
			new IncreasingMaxWithMin(new LinearIncreasingMax(0, 0), maxFrames, maxWords));
		this.number = number;
	}
	/**
	 * 
	 * @param data
	 * @param frameSize
	 * @param preMarkers
	 * @param postMarkers
	 * @param maxWords
	 * @param maxFrames
	 * @param minCount
	 * @param sc
	 */
	protected FunctionalInsteadOfNoun(Corpus<WordAndTagHolder> data,
			int frameSize, int preMarkers, int postMarkers, int maxWords, int maxFrames, int number, Scorer sc)
	{
		super(data, frameSize, preMarkers, postMarkers, sc, new ConvergenceByIterations(3),
			new IncreasingMaxWithMin(new LinearIncreasingMax(0, 0), maxFrames, maxWords));
		this.number = number;
	}
	/* (non-Javadoc)
	 * @see com.gragra.nounIdentification.BaseNounIdentifier#getAssociatedType()
	 */
	@Override
	protected ExtensionInformationValue getAssociatedType()
	{return WordType.Function;}
	/**
	 * 
	 */
	private Int2IntOpenHashMap counts;
	/**
	 * 
	 */
	private final int number;
	/* (non-Javadoc)
	 * @see com.gragra.nounIdentification.BaseNounIdentifier#evaluate(com.gragra.data.Corpus, boolean)
	 */
	@Override
	public double[] evaluate(Corpus<WordAndTagHolder> data, boolean microAverage)
	{
		counts = new Int2IntOpenHashMap();
		for(WordAndTagHolder wth : data)
		{
			ExtensionInformationHolder eih = wth.getExtensionInformation();
			for(int i=0;i<wth.getLength();++i)
			{
				int tag;
				if((tag = eih.getInfiniteInformation(i, ExtensionLevels.ContextTagLevel)) >= 0 && this.decideTypePresentBefore(wth, i))
				{counts.addTo(tag, 1);}
			}
		}
		reduceMap();
		return super.evaluate(data, microAverage);
	}
	/**
	 * 
	 */
	protected void reduceMap()
	{
		Int2IntOpenHashMap map = this.counts;
		Int2ObjectAVLTreeMap<IntSet> list = new Int2ObjectAVLTreeMap<IntSet>();
		IntIterator iit = map.keySet().iterator();
		while(iit.hasNext())
		{
			int i = iit.nextInt();
			int val = -map.get(i);
			IntSet is = list.get(val);
			if(is == null)
			{
				is = new IntOpenHashSet();
				list.put(val, is);
			}
			is.add(i);
		}
		int count = 0;
		map.clear();
		iit = list.keySet().iterator();
		while(count < this.number)
		{
			IntIterator internal = list.get(iit.nextInt()).iterator();
			while(count < this.number && internal.hasNext())
			{
				int c = internal.nextInt();
				map.put(c, ++count);
			}
		}
	}
	/* (non-Javadoc)
	 * @see com.gragra.nounIdentification.BaseNounIdentifier#decideTypePresent(com.gragra.sampling.sequentialStructure.WordAndTagHolder, int)
	 */
	@Override
	protected boolean decideTypePresent(WordAndTagHolder wth, int j)
	{return counts.containsKey(wth.getExtensionInformation().getInfiniteInformation(j, ExtensionLevels.ContextTagLevel));}
	/* (non-Javadoc)
	 * @see com.gragra.nounIdentification.BaseNounIdentifier#annotateCorpusWords(com.gragra.data.Corpus)
	 */
	@Override
	public void annotateCorpusWords(Corpus<WordAndTagHolder> data)
	{
		counts = new Int2IntOpenHashMap();
		for(WordAndTagHolder wth : data)
		{
			ExtensionInformationHolder eih = wth.getExtensionInformation();
			for(int i=0;i<wth.getLength();++i)
			{
				int tag;
				if((tag = eih.getInfiniteInformation(i, ExtensionLevels.ContextTagLevel)) >= 0 && this.decideTypePresentBefore(wth, i))
				{counts.addTo(tag, 1);}
			}
		}
		reduceMap();
		super.annotateCorpusWords(data);
	}
	/* (non-Javadoc)
	 * @see com.gragra.nounIdentification.BaseNounIdentifier#annotateEntryWord(com.gragra.data.Corpus, int)
	 */
	@Override
	protected void annotateEntryWord(Corpus<WordAndTagHolder> data2, int i)
	{
		WordAndTagHolder wth = data2.getEntry(i);
		{
			ExtensionInformationHolder eih = wth.getExtensionInformation();
			for(int k=0;k<wth.getLength();++k)
			{
				if(this.counts.containsKey(eih.getInfiniteInformation(k, ExtensionLevels.ContextTagLevel)))
				{eih.addInformation(k, ExtensionLevels.WordTypeLevel, WordType.Function);}
			}
		}
	}
}